/**
 * LocalStorage.js
 *
 * Released under LGPL License.
 * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
 *
 * License: http://www.tinymce.com/license
 * Contributing: http://www.tinymce.com/contributing
 */

/**
 * This class will simulate LocalStorage on IE 7 and return the native version on modern browsers.
 * Storage is done using userData on IE 7 and a special serialization format. The format is designed
 * to be as small as possible by making sure that the keys and values doesn't need to be encoded. This
 * makes it possible to store for example HTML data.
 *
 * Storage format for userData:
 * <base 32 key length>,<key string>,<base 32 value length>,<value>,...
 *
 * For example this data key1=value1,key2=value2 would be:
 * 4,key1,6,value1,4,key2,6,value2
 *
 * @class tinymce.util.LocalStorage
 * @static
 * @version 4.0
 * @example
 * tinymce.util.LocalStorage.setItem('key', 'value');
 * var value = tinymce.util.LocalStorage.getItem('key');
 */
define(
  'tinymce.core.util.LocalStorage',
  [
  ],
  function () {
    var LocalStorage, storageElm, items, keys, userDataKey, hasOldIEDataSupport;

    // Check for native support
    try {
      if (window.localStorage) {
        return localStorage;
      }
    } catch (ex) {
      // Ignore
    }

    userDataKey = "tinymce";
    storageElm = document.documentElement;
    hasOldIEDataSupport = !!storageElm.addBehavior;

    if (hasOldIEDataSupport) {
      storageElm.addBehavior('#default#userData');
    }

    /**
     * Gets the keys names and updates LocalStorage.length property. Since IE7 doesn't have any getters/setters.
     */
    function updateKeys() {
      keys = [];

      for (var key in items) {
        keys.push(key);
      }

      LocalStorage.length = keys.length;
    }

    /**
     * Loads the userData string and parses it into the items structure.
     */
    function load() {
      var key, data, value, pos = 0;

      items = {};

      // localStorage can be disabled on WebKit/Gecko so make a dummy storage
      if (!hasOldIEDataSupport) {
        return;
      }

      function next(end) {
        var value, nextPos;

        nextPos = end !== undefined ? pos + end : data.indexOf(',', pos);
        if (nextPos === -1 || nextPos > data.length) {
          return null;
        }

        value = data.substring(pos, nextPos);
        pos = nextPos + 1;

        return value;
      }

      storageElm.load(userDataKey);
      data = storageElm.getAttribute(userDataKey) || '';

      do {
        var offset = next();
        if (offset === null) {
          break;
        }

        key = next(parseInt(offset, 32) || 0);
        if (key !== null) {
          offset = next();
          if (offset === null) {
            break;
          }

          value = next(parseInt(offset, 32) || 0);

          if (key) {
            items[key] = value;
          }
        }
      } while (key !== null);

      updateKeys();
    }

    /**
     * Saves the items structure into a the userData format.
     */
    function save() {
      var value, data = '';

      // localStorage can be disabled on WebKit/Gecko so make a dummy storage
      if (!hasOldIEDataSupport) {
        return;
      }

      for (var key in items) {
        value = items[key];
        data += (data ? ',' : '') + key.length.toString(32) + ',' + key + ',' + value.length.toString(32) + ',' + value;
      }

      storageElm.setAttribute(userDataKey, data);

      try {
        storageElm.save(userDataKey);
      } catch (ex) {
        // Ignore disk full
      }

      updateKeys();
    }

    LocalStorage = {
      /**
       * Length of the number of items in storage.
       *
       * @property length
       * @type Number
       * @return {Number} Number of items in storage.
       */
      //length:0,

      /**
       * Returns the key name by index.
       *
       * @method key
       * @param {Number} index Index of key to return.
       * @return {String} Key value or null if it wasn't found.
       */
      key: function (index) {
        return keys[index];
      },

      /**
       * Returns the value if the specified key or null if it wasn't found.
       *
       * @method getItem
       * @param {String} key Key of item to retrieve.
       * @return {String} Value of the specified item or null if it wasn't found.
       */
      getItem: function (key) {
        return key in items ? items[key] : null;
      },

      /**
       * Sets the value of the specified item by it's key.
       *
       * @method setItem
       * @param {String} key Key of the item to set.
       * @param {String} value Value of the item to set.
       */
      setItem: function (key, value) {
        items[key] = "" + value;
        save();
      },

      /**
       * Removes the specified item by key.
       *
       * @method removeItem
       * @param {String} key Key of item to remove.
       */
      removeItem: function (key) {
        delete items[key];
        save();
      },

      /**
       * Removes all items.
       *
       * @method clear
       */
      clear: function () {
        items = {};
        save();
      }
    };

    load();

    return LocalStorage;
  }
);
